#ifdef OBLIVION
#include "Generation.h"

#include "Globals.h"

// Oblivion classes
#include "API/TESForms/BaseFormComponent.h"
#include "API/TESForms/TESObject.h"
#include "API/Items/TESObjectARMO.h"
#include "API/Items/TESObjectCLOT.h"
#include "API/Items/TESObjectWEAP.h"
#include "API/Actors/TESActorBase.h"
#include "API/TESForms/TESObjectREFR.h"
#include "API/TESForms/TESContainer.h"
#include "API/Magic/MagicItemForm.h"
#include "API/Magic/MagicTarget.h"
#include "API/Magic/ActiveEffect.h"
#include "API/Settings/Settings.h"
#include "API/TES/TESDataHandler.h"

// COEF components and utilities
#include "Components/EventManager.h"

// Internal classes
#include "Submodule/MagicItemGenerator.h"
#include "Submodule/AffixFilter.h"
#include "Submodule/AffixList.h"
#include "Submodule/Globals.h"
#include "Submodule/Utilities.h"

std::vector< std::pair<UInt32, UInt32> > Generation::generatedItems = std::vector< std::pair<UInt32, UInt32> >();

bool Generation::CheckObject(const TESBoundObject* base) {
	if ( !base ) {
		_VMESSAGE("NULL pointer passed.");
		return false;
	}

	const TESFullName* hasName = dynamic_cast<const TESFullName*>(base);
	if ( !hasName ) {
		_VMESSAGE("Object &lt;%p&gt; does not have a name.", base->formID);
		return false;
	}

	const TESScriptableForm* canScript = dynamic_cast<const TESScriptableForm*>(base);
	if ( canScript && canScript->script ) {
		_VMESSAGE("Object '%s' &lt;%p&gt; has a script.", hasName->name.c_str(), base->formID);
		return false;
	}

	const TESEnchantableForm* canEnchant = dynamic_cast<const TESEnchantableForm*>(base);
	if ( !canEnchant ) {
		_VMESSAGE("Object '%s' &lt;%p&gt; cannot be enchanted.", hasName->name.c_str(), base->formID);
		return false;
	}

	if ( base->formFlags & TESForm::kFormFlags_QuestItem ) {
		_VMESSAGE("Object '%s' &lt;%p&gt; is a quest item.", hasName->name.c_str(), base->formID);
		return false;
	}

	return true;
}

UInt16 Generation::AddAffixTo(std::vector<EffectItem*>& geis, EnchantmentItem* ench, BSStringT& name, const Affix* affix, Magic::RangeTypes range, bool isRare /*=false*/, UInt8 chrgB /*=0*/) {
	UInt16 charge = affix->mig->Generate(geis, affix->soul, range, chrgB);
	if ( charge == 0 ) {
		_ERROR("Item '%s': error generating prefix &lt;%p&gt; of level %i.", name.c_str(), affix->mig->formID, affix->soul);
		return 0;
	}

	if ( !isRare ) {
		if ( affix->mig->suf )
			name.Format("%s %s", name.c_str(), affix->mig->GetAffix(affix->soul).c_str());
		else
			name.Format("%s %s", affix->mig->GetAffix(affix->soul).c_str(), name.c_str());
	}

	if ( !affix->mig->autocalc )
		ench->SetMagicItemAutoCalc(false);
	
	return charge;
}

UInt16 Generation::GenerateSlot(std::vector<EffectItem*>& geis, EnchantmentItem* ench, BSStringT& name, const AffixList& prefixes, const AffixList& suffixes, Magic::RangeTypes range, bool& isRare /*=false*/) {
	bool hasSuf = flip;
	bool hasPre = (hasSuf ? flip : true);
	
	std::vector<Affix*> affixes = std::vector<Affix*>();
	std::vector<Affix*> negixes = std::vector<Affix*>();
	std::vector<Affix*>::size_type i;
	
	if ( hasPre && prefixes.sumChances > 0 ) {
		Affix* affix = prefixes.chooseRandom();
		if ( affix->soul == Souls::kSoulIndex_Negative ) {
			negixes.push_back(affix);
		}
		else {
			affixes.push_back(affix);
		}
	}

	if ( hasSuf && suffixes.sumChances > 0 ) {
		Affix* affix = suffixes.chooseRandom();
		if ( affix->soul == Souls::kSoulIndex_Negative ) {
			negixes.push_back(affix);
		}
		else {
			affixes.push_back(affix);
		}
	}

	if ( affixes.size() == 0 && negixes.size() > 0 ) {
		if ( negixes[0]->mig->suf ) {
			if ( prefixes.hasPositive() ) {
				Affix* affix = prefixes.chooseRandom();
				while (affix->soul == Souls::kSoulIndex_Negative)
					affix = prefixes.chooseRandom();
				affixes.push_back(affix);
			}
			else if ( suffixes.hasPositive() ) {
				Affix* affix = suffixes.chooseRandom();
				while (affix->soul == Souls::kSoulIndex_Negative)
					affix = suffixes.chooseRandom();
				affixes.push_back(affix);
				negixes.erase(negixes.begin());
			}
			else {
				throw std::exception("did not generate any positive affixes.");
			}
		}
		else {
			if ( suffixes.hasPositive() ) {
				Affix* affix = suffixes.chooseRandom();
				while (affix->soul == Souls::kSoulIndex_Negative)
					affix = suffixes.chooseRandom();
				affixes.push_back(affix);
			}
			else if ( prefixes.hasPositive() ) {
				Affix* affix = prefixes.chooseRandom();
				while (affix->soul == Souls::kSoulIndex_Negative)
					affix = prefixes.chooseRandom();
				affixes.push_back(affix);
				negixes.erase(negixes.begin());
			}
			else {
				throw std::exception("did not generate any positive affixes.");
			}
		}
	}
	
	UInt16 charge = 0;
	
	if ( affixes.size() > 0 ) {
		UInt16 negch = 0;
		isRare = isRare || (affixes.size() + negixes.size() > 2);
		for ( i = 0; i < negixes.size(); ++i )
			negch += AddAffixTo(geis, ench, name, negixes[i], range, isRare);

		for ( i = 0; i < affixes.size(); ++i )
			charge += AddAffixTo(geis, ench, name, affixes[i], range, isRare, double(negch)/affixes.size()+0.5);
	}
	else
		throw std::exception("did not get any affixes.");

	return charge;
}

void Generation::NameRareItem(BSStringT& name, const std::vector<AffixFilter*>& flts) {
	if ( flts.size() <= 0 ) {
		_ERROR("Empty filter list?");
		return;
	}

	UInt8 firsts = 0;
	for ( UInt8 i = 0; i < flts.size(); ++i ) firsts += flts[i]->GetNumRareFirstNames();
	if ( firsts == 0 ) {
		_ERROR("Empty rare first name list?");
		return;
	}

	boost::uniform_int<UInt8> firstRange (0, firsts-1);
	boost::variate_generator<boost::mt19937&, boost::uniform_int<UInt8> >genFirst(rng, firstRange);
	UInt8 first = genFirst();

	UInt8 flt;
	for ( flt = 0; flts[flt]->GetNumRareFirstNames() <= first; first -= flts[flt++]->GetNumRareFirstNames() );
	BSStringT rareFirst = flts[flt]->GetNthRareFirstName(first);

	UInt8 seconds = 0;
	for ( UInt8 i = 0; i < flts.size(); ++i ) seconds += flts[i]->GetNumRareSecondNames();
	if ( seconds == 0 ) {
		_ERROR("Empty rare second name list?");
		return;
	}

	boost::uniform_int<UInt8> secondRange (0, seconds-1);
	boost::variate_generator<boost::mt19937&, boost::uniform_int<UInt8> >genSecond(rng, secondRange);
	UInt8 second = genSecond();

	for ( flt = 0; flts[flt]->GetNumRareSecondNames() <= second; second -= flts[flt++]->GetNumRareSecondNames() );
	BSStringT rareSecond = flts[flt]->GetNthRareSecondName(second);

	name.Format("%s %s %s", rareFirst.c_str(), rareSecond.c_str(), name.c_str());
}

void Generation::ApplyEffectsTo(EnchantmentItem* ench, std::vector<EffectItem*>& geis) {
	ench->baseCost = 0;
	for ( std::vector<EffectItem*>::size_type i = 0; i < geis.size(); ++i ) {
		for ( std::vector<EffectItem*>::size_type j = i + 1; j < geis.size(); ++j ) {
			if ( geis[i] && geis[j] ) {
				if ( geis[i]->effect->mgefCode == geis[j]->effect->mgefCode && geis[i]->duration == geis[j]->duration && geis[i]->area == geis[j]->area ) {
					geis[i]->magnitude += geis[j]->magnitude;
					delete geis[j];
					geis.erase(geis.begin()+(j--));
				}
			}
		}
		if ( geis[i] ) {
			ench->AddEffect(geis[i]);
			ench->baseCost += geis[i]->MagickaCost()+0.5;
		}
	}
	if ( ench->baseCost < 1 )
		ench->baseCost = 1;
}

template<typename T>
bool Generation::GetAffixList(const T* base, std::vector<AffixFilter*>*& filters, AffixList& prefixes, AffixList& suffixes, UInt16 level, bool LtEq /*=true*/) {
	if ( filters ) {
		for ( std::vector<AffixFilter*>::iterator i = filters->begin(); i != filters->end(); ++i ) {
			AffixFilter* flt = dynamic_cast<AffixFilter*>(*i);
			if ( flt ) {
				if ( flt->Check(base) ) {
					if ( flt->SortAffixes(prefixes, suffixes, level, LtEq) ) {
						filters->push_back(flt);
					}
					else {
						_ERROR("Some error in sorting affixes from AffixFilter &lt;%p&gt;?", flt->formID);
					}
				}
			}
			else // i->data is wrong polymorphic type, which is just lolwut.
				_ERROR("Bad AffixFilter &lt;%p&gt;?", (*i)->formID);
		}
	}
	else {
		filters = new std::vector<AffixFilter*>();
		for ( BSSimpleList<TESForm*>::Node* i = &AffixFilter::extendedForm.FormList().firstNode; i && i->data; i = i->next ) {
			AffixFilter* flt = dynamic_cast<AffixFilter*>(i->data);
			if ( flt ) {
				if ( flt->Check(base) ) {
					if ( flt->SortAffixes(prefixes, suffixes, level, LtEq) ) {
						filters->push_back(flt);
					}
					else {
						_ERROR("Some error in sorting affixes from AffixFilter &lt;%p&gt;?", flt->formID);
					}
				}
			}
			else // i->data is wrong polymorphic type, which is just lolwut.
				_ERROR("Bad AffixFilter &lt;%p&gt;?", i->data->formID);
		}
	}
	return filters && !(filters->empty());
}

bool Generation::GetAffixList(const TESBoundObject* base, std::vector<AffixFilter*>*& filters, AffixList& prefixes, AffixList& suffixes, UInt16 level, bool LtEq /*=true*/) {
	const TESObjectARMO* armor = dynamic_cast<const TESObjectARMO*>(base);
	if ( armor )
		return GetAffixList<TESObjectARMO>(armor, filters, prefixes, suffixes, level, LtEq);

	const TESObjectCLOT* clothing = dynamic_cast<const TESObjectCLOT*>(base);
	if ( clothing )
		return GetAffixList<TESObjectCLOT>(clothing, filters, prefixes, suffixes, level, LtEq);

	const TESObjectWEAP* weapon = dynamic_cast<const TESObjectWEAP*>(base);
	if ( weapon )
		return GetAffixList<TESObjectWEAP>(weapon, filters, prefixes, suffixes, level, LtEq);

	return false;
}

bool Generation::GetAffixList(const TESObjectARMO* base, std::vector<AffixFilter*>*& filters, AffixList& prefixes, AffixList& suffixes, UInt16 level, bool LtEq /*=true*/) {
	return GetAffixList<TESObjectARMO>(base, filters, prefixes, suffixes, level, LtEq);
}

bool Generation::GetAffixList(const TESObjectCLOT* base, std::vector<AffixFilter*>*& filters, AffixList& prefixes, AffixList& suffixes, UInt16 level, bool LtEq /*=true*/) {
	return GetAffixList<TESObjectCLOT>(base, filters, prefixes, suffixes, level, LtEq);
}

bool Generation::GetAffixList(const TESObjectWEAP* base, std::vector<AffixFilter*>*& filters, AffixList& prefixes, AffixList& suffixes, UInt16 level, bool LtEq /*=true*/) {
	return GetAffixList<TESObjectWEAP>(base, filters, prefixes, suffixes, level, LtEq);
}

template<typename T>
T* initCopy(const T* base) {
	T* toEnch = dynamic_cast<T*>(const_cast<T*>(base)->Clone(true, NULL));
	if ( !toEnch ) {
		_ERROR("Item '%s' &lt;%p&gt; was not copied correctly.", base->name.c_str(), base->formID);
		return NULL;
	}

	toEnch->enchantment = dynamic_cast<EnchantmentItem*>(TESDataHandler::CreateForm(TESForm::kFormType_Enchantment));
	if ( !toEnch->enchantment ) {
		_ERROR("Item '%s' &lt;%p&gt; did not gain a new enchantment correctly.", toEnch->name.c_str(), toEnch->formID);
		delete toEnch;
		return NULL;
	}
	toEnch->enchantment->SetMagicItemAutoCalc(true);
	toEnch->enchantment->enchType = getCastType(base);

	return toEnch;
}

Magic::CastTypes getCastType(const TESObjectARMO* armor) {
	return Magic::kCast_Constant;
}

Magic::CastTypes getCastType(const TESObjectCLOT* clothing) {
	return Magic::kCast_Constant;
}

Magic::CastTypes getCastType(const TESObjectWEAP* weapon) {
	return Magic::kCast_WhenStrikes;
}

UInt8 getSlots(const TESBipedModelForm* biped) {
	UInt8 slots = 0;
	for ( UInt8 i = 0; i < TESBipedModelForm::kSlotIndex__MAX; ++i )
		if ( (biped->bipedSlotMask & (1<<i)) != 0 )
			++slots;
	slots = max(slots, 1);

	if ( slots > 1 ) {
		boost::uniform_int<UInt8> slotRng(1, slots);
		boost::variate_generator<boost::mt19937, boost::uniform_int<UInt8> > slotGen(rng, slotRng);
		slots = slotGen();
	}

	return slots;
}

UInt8 getSlots(TESObjectWEAP* weapon) {
	UInt8 slots;
	switch (weapon->weaponType) {			
		case TESObjectWEAP::kWeaponType_Staff:
		case TESObjectWEAP::kWeaponType_BladeTwoHand:
		case TESObjectWEAP::kWeaponType_BluntTwoHand:
		case TESObjectWEAP::kWeaponType_Bow:
			slots = flip?2:1;
			break;
			
		case TESObjectWEAP::kWeaponType_BladeOneHand:
		case TESObjectWEAP::kWeaponType_BluntOneHand:
		default:
			slots = 1;
			break;
	}
	return slots;
}

Magic::RangeTypes getRange(const TESObjectARMO* armor) {
	armor->enchantment->enchType = Magic::kCast_Constant;
	return Magic::kRange_Self;
}

Magic::RangeTypes getRange(const TESObjectCLOT* clothing) {
	clothing->enchantment->enchType = Magic::kCast_Constant;
	return Magic::kRange_Self;
}

Magic::RangeTypes getRange(const TESObjectWEAP* weapon) {
	if ( weapon->weaponType == TESObjectWEAP::kWeaponType_Staff ) {
		weapon->enchantment->enchType = Magic::kCast_WhenUsed;
		return Magic::kRange_Target;
	}
	else {
		weapon->enchantment->enchType = Magic::kCast_WhenStrikes;
		return Magic::kRange_Touch;
	}
}

template<typename T>
T* Generation::Enchant(const T* base, UInt16 level, bool LtEq /*=true*/, std::vector<AffixFilter*>* flts /*=NULL*/) {
	if ( !base ) {
		_ERROR("NULL pointer to base item?");
		return NULL;
	}

	AffixList prefixes = AffixList(), suffixes = AffixList();
	if ( !GetAffixList<T>(base, flts, prefixes, suffixes, level, LtEq) ) {
		_VMESSAGE("Armor '%s' &lt;%p&gt; does not pass any affix filters.", base->name.c_str(), base->formID);
		return NULL;
	}

	T* toEnch = initCopy(base);
	
	UInt16 charge = 0;
	std::vector<EffectItem*> geis = std::vector<EffectItem*>();

	UInt8 slots = getSlots(toEnch);
	Magic::RangeTypes range = getRange(toEnch);

	bool isRare = slots > 1;
	for ( UInt8 i = 0; i < slots; ++i ) {
		try {
			charge += GenerateSlot(geis, toEnch->enchantment, toEnch->name, prefixes, suffixes, range, isRare);
		}
		catch (std::exception e) {
			_ERROR("Item '%s' &lt;%p&gt; %s", toEnch->name.c_str(), toEnch->formID, e.what());
			delete toEnch->enchantment;
			delete toEnch;
			return NULL;
		}
	}

	if ( charge <= 0 ) {
		_ERROR("Item '%s' &lt;%p&gt; gained 0 charge worth of enchantments.", base->name.c_str(), toEnch->formID);
		delete toEnch->enchantment;
		delete toEnch;
		return NULL;
	}

	if ( isRare ) {
		NameRareItem(toEnch->name, *flts);
	}
	_VMESSAGE("Item '%s' &lt;%p&gt; enchanted as '%s' &lt;%p&gt;.", base->name.c_str(), base->formID, toEnch->name.c_str(), toEnch->formID);

	ApplyEffectsTo(toEnch->enchantment, geis);

	toEnch->enchantment->charge = charge;
	toEnch->enchantmentCharge = charge;

	generatedItems.push_back(std::pair<UInt32, UInt32>(base->formID, toEnch->formID));

	delete flts;

	return toEnch;
}

TESBoundObject* Generation::Enchant(const TESBoundObject* base, UInt16 level, bool LtEq /*=true*/, std::vector<AffixFilter*>* flts /*=NULL*/) {
	if (CheckObject(base)) {
		const TESObjectARMO* armor = dynamic_cast<const TESObjectARMO*>(base);
		if ( armor )
			return Enchant<TESObjectARMO>(armor, level, LtEq, flts);

		const TESObjectCLOT* clothing = dynamic_cast<const TESObjectCLOT*>(base);
		if ( clothing )
			return Enchant<TESObjectCLOT>(clothing, level, LtEq, flts);

		const TESObjectWEAP* weapon = dynamic_cast<const TESObjectWEAP*>(base);
		if ( weapon )
			return Enchant<TESObjectWEAP>(weapon, level, LtEq, flts);

		return NULL;
	}
	else {
		return NULL;
	}
}

TESObjectARMO* Generation::Enchant(const TESObjectARMO* base, UInt16 level, bool LtEq /*=true*/, std::vector<AffixFilter*>* flts /*=NULL*/) {
	return Enchant<TESObjectARMO>(base, level, LtEq, flts);
}

TESObjectCLOT* Generation::Enchant(const TESObjectCLOT* base, UInt16 level, bool LtEq /*=true*/, std::vector<AffixFilter*>* flts /*=NULL*/) {
	return Enchant<TESObjectCLOT>(base, level, LtEq, flts);
}

TESObjectWEAP* Generation::Enchant(const TESObjectWEAP* base, UInt16 level, bool LtEq /*=true*/, std::vector<AffixFilter*>* flts /*=NULL*/) {
	return Enchant<TESObjectWEAP>(base, level, LtEq, flts);
}

TESActorBase* Generation::player = NULL;
TESObjectREFR* Generation::playerRef = NULL;

void Generation::ProcessContainer(ContainerExtraData* extraData, UInt32 level) {
	if ( !player )
		player = dynamic_cast<TESActorBase*>(TESForm::LookupByFormID(0x7)); // hardcoded default player form

	if ( !playerRef )
		playerRef = dynamic_cast<TESObjectREFR*>(TESForm::LookupByFormID(0x14)); // hardcoded default playerRef form

	if ( !extraData ) {
		_ERROR("No extra data.");
		return;
	}

	TESObjectREFR* ref = extraData->parent;
	if ( !ref ) {
		_ERROR("No ref.");
		return;
	}
	else if ( ref == playerRef ) {
		return;
	}
	else if ( ref->GetItemCount(*token) > 0 ) {
		_VMESSAGE("Container &lt;%p&gt; already processed.", ref->formID);
		return;
	}

	TESContainer* container = ref->GetContainer();
	if ( !container ) {
		_ERROR("No container.");
		return;
	}

	TESActorBase* actor = dynamic_cast<TESActorBase*>(ref->baseForm);
	UInt16 lv = 0, luck = 0;
	SInt32 amf = 0, pmf = 0;
	if ( playerRef ) {
		for ( auto i = &playerRef->GetMagicTarget()->GetActiveEffectList()->firstNode; i && i->data; i = i->next ) {
			if ( i->data->effectItem->effect == FOMF ) {
				pmf += i->data->effectItem->magnitude;
			}
			else if ( i->data->effectItem->effect == DRMF ) {
				pmf -= i->data->effectItem->magnitude;
			}
		}
	}

	if ( actor ) {
		lv = actor->GetLevel();
		luck = actor->GetAViBase(ActorValues::kActorVal_Luck);
		for ( auto i = &ref->GetMagicTarget()->GetActiveEffectList()->firstNode; i && i->data; i = i->next ) {
			if ( i->data->effectItem->effect == FOMF ) {
				amf += i->data->effectItem->magnitude;
			}
			else if ( i->data->effectItem->effect == DRMF ) {
				amf -= i->data->effectItem->magnitude;
			}
		}
	}
	else {
		if ( player ) {
			lv = player->GetLevel();
			luck = player->GetAViBase(ActorValues::kActorVal_Luck);
		}
		else {
			_WARNING("No scaling data available; using blind defaults.");
			lv = level;
			luck = 50;
		}
	}
	if ( lv < 1 ) {
//		_VMESSAGE("Setting level to minimum 1.");
		lv = 1;
	}

	double chance;
	if ( fAutoEnchantmentRateBase && fAutoEnchantmentRateMult )
		chance = (100*fAutoEnchantmentRateBase->value.f + fAutoEnchantmentRateMult->value.f * (luck-50)) * (1+pmf/100) * (1+amf/100);
	else // use default values
		chance = (2 + 0.01 * (luck-50)) * (1+pmf/100) * (1+amf/100);
	
	std::map<TESBoundObject*, TESBoundObject*> enchd;

	for ( auto entry = &container->contents.firstNode; entry && entry->data; entry = entry->next ) {
		double roll = pct();
		if ( roll < chance ) {
			TESBoundObject* old_obj = dynamic_cast<TESBoundObject*>(entry->data->form);
			if ( old_obj ) {
				TESBoundObject* new_obj = Enchant(old_obj, lv);
				if ( new_obj ) {
					enchd.insert(std::pair<TESBoundObject*, TESBoundObject*>(old_obj, new_obj));
				}
			}
		}
	}

	for ( std::map<TESBoundObject*, TESBoundObject*>::iterator i = enchd.begin(); i != enchd.end(); ++i ) {
		SInt32 oct = ref->GetItemCount(*i->first);
		if ( oct > 0 ) {
			ref->RemoveItem(i->first, NULL, 1, false, false, NULL, NULL, NULL, true, true);
			SInt32 nct = ref->GetItemCount(*i->first);

			ref->AddItem(i->second, NULL, 1);
			SInt32 ect = ref->GetItemCount(*i->second);
			if ( ect > 0 ) {
				if ( addFormToSavegame(i->second->formID) ) {
					_VMESSAGE("Removed 1 &lt;%p&gt; (from %i to %i) from ref &lt;%p&gt;, added 1 &lt;%p&gt; (from 0 to %i), and added it to the savegame.", i->first->formID, oct, nct, ref->formID, i->second->formID, ect);
				}
				else {
					_VMESSAGE("Removed 1 &lt;%p&gt; (from %i to %i) from ref &lt;%p&gt;, added 1 &lt;%p&gt; (from 0 to %i), but DID NOT add it to the savegame.", i->first->formID, oct, nct, ref->formID, i->second->formID, ect);
				}
			}
			else {
				_VMESSAGE("Removed 1 &lt;%p&gt; (from %i to %i) from ref &lt;%p&gt;, but adding 1 &lt;%p&gt; failed.", i->first->formID, oct, nct, ref->formID, i->second->formID, ect);
			}
		}
		else {
			_ERROR("Container does not have item to be enchanted &lt;%p&gt;?", i->first->formID);
		}
	}
	ref->AddItem(token, NULL, 1);
}
#endif